home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / stk-3.002 / stk-3 / STk-3.1 / Tk / generic / tkPlace.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-23  |  31.7 KB  |  1,133 lines

  1. /* 
  2.  * tkPlace.c --
  3.  *
  4.  *    This file contains code to implement a simple geometry manager
  5.  *    for Tk based on absolute placement or "rubber-sheet" placement.
  6.  *
  7.  * Copyright (c) 1992-1994 The Regents of the University of California.
  8.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  *
  13.  * SCCS: @(#) tkPlace.c 1.25 96/02/15 18:52:32
  14.  */
  15.  
  16. #include "tkPort.h"
  17. #include "tkInt.h"
  18.  
  19. /*
  20.  * Border modes for relative placement:
  21.  *
  22.  * BM_INSIDE:        relative distances computed using area inside
  23.  *            all borders of master window.
  24.  * BM_OUTSIDE:        relative distances computed using outside area
  25.  *            that includes all borders of master.
  26.  * BM_IGNORE:        border issues are ignored:  place relative to
  27.  *            master's actual window size.
  28.  */
  29.  
  30. typedef enum {BM_INSIDE, BM_OUTSIDE, BM_IGNORE} BorderMode;
  31.  
  32. /*
  33.  * For each window whose geometry is managed by the placer there is
  34.  * a structure of the following type:
  35.  */
  36.  
  37. typedef struct Slave {
  38.     Tk_Window tkwin;        /* Tk's token for window. */
  39.     struct Master *masterPtr;    /* Pointer to information for window
  40.                  * relative to which tkwin is placed.
  41.                  * This isn't necessarily the logical
  42.                  * parent of tkwin.  NULL means the
  43.                  * master was deleted or never assigned. */
  44.     struct Slave *nextPtr;    /* Next in list of windows placed relative
  45.                  * to same master (NULL for end of list). */
  46.  
  47.     /*
  48.      * Geometry information for window;  where there are both relative
  49.      * and absolute values for the same attribute (e.g. x and relX) only
  50.      * one of them is actually used, depending on flags.
  51.      */
  52.  
  53.     int x, y;            /* X and Y pixel coordinates for tkwin. */
  54.     float relX, relY;        /* X and Y coordinates relative to size of
  55.                  * master. */
  56.     int width, height;        /* Absolute dimensions for tkwin. */
  57.     float relWidth, relHeight;    /* Dimensions for tkwin relative to size of
  58.                  * master. */
  59.     Tk_Anchor anchor;        /* Which point on tkwin is placed at the
  60.                  * given position. */
  61.     BorderMode borderMode;    /* How to treat borders of master window. */
  62.     int flags;            /* Various flags;  see below for bit
  63.                  * definitions. */
  64. } Slave;
  65.  
  66. /*
  67.  * Flag definitions for Slave structures:
  68.  *
  69.  * CHILD_WIDTH -        1 means -width was specified;
  70.  * CHILD_REL_WIDTH -        1 means -relwidth was specified.
  71.  * CHILD_HEIGHT -        1 means -height was specified;
  72.  * CHILD_REL_HEIGHT -        1 means -relheight was specified.
  73.  */
  74.  
  75. #define CHILD_WIDTH        1
  76. #define CHILD_REL_WIDTH        2
  77. #define CHILD_HEIGHT        4
  78. #define CHILD_REL_HEIGHT    8
  79.  
  80. /*
  81.  * For each master window that has a slave managed by the placer there
  82.  * is a structure of the following form:
  83.  */
  84.  
  85. typedef struct Master {
  86.     Tk_Window tkwin;        /* Tk's token for master window. */
  87.     struct Slave *slavePtr;    /* First in linked list of slaves
  88.                  * placed relative to this master. */
  89.     int flags;            /* See below for bit definitions. */
  90. } Master;
  91.  
  92. /*
  93.  * Flag definitions for masters:
  94.  *
  95.  * PARENT_RECONFIG_PENDING -    1 means that a call to RecomputePlacement
  96.  *                is already pending via a Do_When_Idle handler.
  97.  */
  98.  
  99. #define PARENT_RECONFIG_PENDING    1
  100.  
  101. /*
  102.  * The hash tables below both use Tk_Window tokens as keys.  They map
  103.  * from Tk_Windows to Slave and Master structures for windows, if they
  104.  * exist.
  105.  */
  106.  
  107. static int initialized = 0;
  108. static Tcl_HashTable masterTable;
  109. static Tcl_HashTable slaveTable;
  110. /*
  111.  * The following structure is the official type record for the
  112.  * placer:
  113.  */
  114.  
  115. static void        PlaceRequestProc _ANSI_ARGS_((ClientData clientData,
  116.                 Tk_Window tkwin));
  117. static void        PlaceLostSlaveProc _ANSI_ARGS_((ClientData clientData,
  118.                 Tk_Window tkwin));
  119.  
  120. static Tk_GeomMgr placerType = {
  121.     "place",                /* name */
  122.     PlaceRequestProc,            /* requestProc */
  123.     PlaceLostSlaveProc,            /* lostSlaveProc */
  124. };
  125.  
  126. /*
  127.  * Forward declarations for procedures defined later in this file:
  128.  */
  129.  
  130. static void        SlaveStructureProc _ANSI_ARGS_((ClientData clientData,
  131.                 XEvent *eventPtr));
  132. static int        ConfigureSlave _ANSI_ARGS_((Tcl_Interp *interp,
  133.                 Slave *slavePtr, int argc, char **argv));
  134. static Slave *        FindSlave _ANSI_ARGS_((Tk_Window tkwin));
  135. static Master *        FindMaster _ANSI_ARGS_((Tk_Window tkwin));
  136. static void        MasterStructureProc _ANSI_ARGS_((ClientData clientData,
  137.                 XEvent *eventPtr));
  138. static void        RecomputePlacement _ANSI_ARGS_((ClientData clientData));
  139. static void        UnlinkSlave _ANSI_ARGS_((Slave *slavePtr));
  140.  
  141. /*
  142.  *--------------------------------------------------------------
  143.  *
  144.  * Tk_PlaceCmd --
  145.  *
  146.  *    This procedure is invoked to process the "place" Tcl
  147.  *    commands.  See the user documentation for details on
  148.  *    what it does.
  149.  *
  150.  * Results:
  151.  *    A standard Tcl result.
  152.  *
  153.  * Side effects:
  154.  *    See the user documentation.
  155.  *
  156.  *--------------------------------------------------------------
  157.  */
  158.  
  159. int
  160. Tk_PlaceCmd(clientData, interp, argc, argv)
  161.     ClientData clientData;    /* Main window associated with interpreter. */
  162.     Tcl_Interp *interp;        /* Current interpreter. */
  163.     int argc;            /* Number of arguments. */
  164.     char **argv;        /* Argument strings. */
  165. {
  166.     Tk_Window tkwin;
  167.     Slave *slavePtr;
  168.     Tcl_HashEntry *hPtr;
  169.     size_t length;
  170.     int c;
  171.  
  172.     /*
  173.      * Initialize, if that hasn't been done yet.
  174.      */
  175.  
  176.     if (!initialized) {
  177.     Tcl_InitHashTable(&masterTable, TCL_ONE_WORD_KEYS);
  178.     Tcl_InitHashTable(&slaveTable, TCL_ONE_WORD_KEYS);
  179.     initialized = 1;
  180.     }
  181.  
  182.     if (argc < 3) {
  183.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  184.         argv[0], " option|pathName args", (char *) NULL);
  185.     return TCL_ERROR;
  186.     }
  187.     c = argv[1][0];
  188.     length = strlen(argv[1]);
  189.  
  190.     /*
  191.      * Handle special shortcut where window name is first argument.
  192.      */
  193.  
  194.     if (c == '.') {
  195.     tkwin = Tk_NameToWindow(interp, argv[1], (Tk_Window) clientData);
  196.     if (tkwin == NULL) {
  197.         return TCL_ERROR;
  198.     }
  199.     slavePtr = FindSlave(tkwin);
  200.     return ConfigureSlave(interp, slavePtr, argc-2, argv+2);
  201.     }
  202.  
  203.     /*
  204.      * Handle more general case of option followed by window name followed
  205.      * by possible additional arguments.
  206.      */
  207.  
  208.     tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData);
  209.     if (tkwin == NULL) {
  210.     return TCL_ERROR;
  211.     }
  212.     if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
  213.     if (argc < 5) {
  214.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  215.             argv[0],
  216.             " configure pathName option value ?option value ...?\"",
  217.             (char *) NULL);
  218.         return TCL_ERROR;
  219.     }
  220.     slavePtr = FindSlave(tkwin);
  221.     return ConfigureSlave(interp, slavePtr, argc-3, argv+3);
  222.     } else if ((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) {
  223.     if (argc != 3) {
  224.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  225.             argv[0], " forget pathName\"", (char *) NULL);
  226.         return TCL_ERROR;
  227.     }
  228.     hPtr = Tcl_FindHashEntry(&slaveTable, (char *) tkwin);
  229.     if (hPtr == NULL) {
  230.         return TCL_OK;
  231.     }
  232.     slavePtr = (Slave *) Tcl_GetHashValue(hPtr);
  233.     if ((slavePtr->masterPtr != NULL) &&
  234.         (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin))) {
  235.         Tk_UnmaintainGeometry(slavePtr->tkwin,
  236.             slavePtr->masterPtr->tkwin);
  237.     }
  238.     UnlinkSlave(slavePtr);
  239.     Tcl_DeleteHashEntry(hPtr);
  240.     Tk_DeleteEventHandler(tkwin, StructureNotifyMask, SlaveStructureProc,
  241.         (ClientData) slavePtr);
  242.     Tk_ManageGeometry(tkwin, (Tk_GeomMgr *) NULL, (ClientData) NULL);
  243.     Tk_UnmapWindow(tkwin);
  244.     ckfree((char *) slavePtr);
  245.     } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
  246.     char buffer[50];
  247.  
  248.     if (argc != 3) {
  249.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  250.             argv[0], " info pathName\"", (char *) NULL);
  251.         return TCL_ERROR;
  252.     }
  253.     hPtr = Tcl_FindHashEntry(&slaveTable, (char *) tkwin);
  254.     if (hPtr == NULL) {
  255.         return TCL_OK;
  256.     }
  257.     slavePtr = (Slave *) Tcl_GetHashValue(hPtr);
  258. #ifdef STk_CODE
  259.     sprintf(buffer, ":x %d", slavePtr->x);
  260. #else
  261.     sprintf(buffer, "-x %d", slavePtr->x);
  262. #endif
  263.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  264. #ifdef STk_CODE
  265.     sprintf(buffer, " :relx %.4g", slavePtr->relX);
  266. #else
  267.     sprintf(buffer, " -relx %.4g", slavePtr->relX);
  268. #endif
  269.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  270. #ifdef STk_CODE
  271.     sprintf(buffer, " :y %d", slavePtr->y);
  272. #else
  273.     sprintf(buffer, " -y %d", slavePtr->y);
  274. #endif
  275.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  276. #ifdef STk_CODE
  277.     sprintf(buffer, " :rely %.4g", slavePtr->relY);
  278. #else
  279.     sprintf(buffer, " -rely %.4g", slavePtr->relY);
  280. #endif
  281.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  282.     if (slavePtr->flags & CHILD_WIDTH) {
  283. #ifdef STk_CODE
  284.         sprintf(buffer, " :width %d", slavePtr->width);
  285. #else
  286.         sprintf(buffer, " -width %d", slavePtr->width);
  287. #endif
  288.         Tcl_AppendResult(interp, buffer, (char *) NULL);
  289.     } else {
  290. #ifdef STk_CODE
  291.         Tcl_AppendResult(interp, " :width \"\"", (char *) NULL);
  292. #else
  293.         Tcl_AppendResult(interp, " -width {}", (char *) NULL);
  294. #endif
  295.     }
  296.     if (slavePtr->flags & CHILD_REL_WIDTH) {
  297. #ifdef STk_CODE
  298.         sprintf(buffer, " :relwidth %.4g", slavePtr->relWidth);
  299. #else
  300.         sprintf(buffer, " -relwidth %.4g", slavePtr->relWidth);
  301. #endif
  302.         Tcl_AppendResult(interp, buffer, (char *) NULL);
  303.     } else {
  304. #ifdef STk_CODE
  305.         Tcl_AppendResult(interp, " :relwidth \"\"", (char *) NULL);
  306. #else
  307.         Tcl_AppendResult(interp, " -relwidth {}", (char *) NULL);
  308. #endif
  309.     }
  310.     if (slavePtr->flags & CHILD_HEIGHT) {
  311. #ifdef STk_CODE
  312.         sprintf(buffer, " :height %d", slavePtr->height);
  313. #else
  314.         sprintf(buffer, " -height %d", slavePtr->height);
  315. #endif
  316.         Tcl_AppendResult(interp, buffer, (char *) NULL);
  317.     } else {
  318. #ifdef STk_CODE
  319.         Tcl_AppendResult(interp, " :height \"\"", (char *) NULL);
  320. #else
  321.         Tcl_AppendResult(interp, " -height {}", (char *) NULL);
  322. #endif
  323.     }
  324.     if (slavePtr->flags & CHILD_REL_HEIGHT) {
  325. #ifdef STk_CODE
  326.         sprintf(buffer, " :relheight %.4g", slavePtr->relHeight);
  327. #else
  328.         sprintf(buffer, " -relheight %.4g", slavePtr->relHeight);
  329. #endif
  330.         Tcl_AppendResult(interp, buffer, (char *) NULL);
  331.     } else {
  332. #ifdef STk_CODE
  333.         Tcl_AppendResult(interp, " :relheight \"\"", (char *) NULL);
  334. #else
  335.         Tcl_AppendResult(interp, " -relheight {}", (char *) NULL);
  336. #endif
  337.     }
  338.  
  339. #ifdef STk_CODE
  340.     Tcl_AppendResult(interp, 
  341.              " :anchor \"", Tk_NameOfAnchor(slavePtr->anchor), "\"",
  342. #else
  343.     Tcl_AppendResult(interp, " -anchor ", Tk_NameOfAnchor(slavePtr->anchor),
  344. #endif
  345.         (char *) NULL);
  346.     if (slavePtr->borderMode == BM_OUTSIDE) {
  347. #ifdef STk_CODE
  348.         Tcl_AppendResult(interp, " :bordermode \"outside\"", (char *) NULL);
  349. #else
  350.         Tcl_AppendResult(interp, " -bordermode outside", (char *) NULL);
  351. #endif
  352.     } else if (slavePtr->borderMode == BM_IGNORE) {
  353. #ifdef STk_CODE
  354.         Tcl_AppendResult(interp, " :bordermode \"ignore\"", (char *) NULL);
  355. #else
  356.         Tcl_AppendResult(interp, " -bordermode ignore", (char *) NULL);
  357. #endif
  358.     }
  359.     if ((slavePtr->masterPtr != NULL)
  360.         && (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin))) {
  361. #ifdef STk_CODE
  362.         Tcl_AppendResult(interp, " :in #.",
  363.             Tk_PathName(slavePtr->masterPtr->tkwin), (char *) NULL);
  364. #else
  365.         Tcl_AppendResult(interp, " -in ",
  366.             Tk_PathName(slavePtr->masterPtr->tkwin), (char *) NULL);
  367. #endif
  368.     }
  369.     } else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0)) {
  370.     if (argc != 3) {
  371.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  372.             argv[0], " slaves pathName\"", (char *) NULL);
  373.         return TCL_ERROR;
  374.     }
  375.     hPtr = Tcl_FindHashEntry(&masterTable, (char *) tkwin);
  376. #ifdef STk_CODE
  377.     Tcl_AppendElement(interp, "(");
  378. #endif
  379.     if (hPtr != NULL) {
  380.         Master *masterPtr;
  381.         masterPtr = (Master *) Tcl_GetHashValue(hPtr);
  382.         for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  383.             slavePtr = slavePtr->nextPtr) {
  384.         Tcl_AppendElement(interp, Tk_PathName(slavePtr->tkwin));
  385.         }
  386. #ifdef STk_CODE
  387.     Tcl_AppendElement(interp, ")");
  388. #endif
  389.     }
  390.     } else {
  391.     Tcl_AppendResult(interp, "unknown or ambiguous option \"", argv[1],
  392.         "\": must be configure, forget, info, or slaves",
  393.         (char *) NULL);
  394.     return TCL_ERROR;
  395.     }
  396.     return TCL_OK;
  397. }
  398.  
  399. /*
  400.  *----------------------------------------------------------------------
  401.  *
  402.  * FindSlave --
  403.  *
  404.  *    Given a Tk_Window token, find the Slave structure corresponding
  405.  *    to that token (making a new one if necessary).
  406.  *
  407.  * Results:
  408.  *    None.
  409.  *
  410.  * Side effects:
  411.  *    A new Slave structure may be created.
  412.  *
  413.  *----------------------------------------------------------------------
  414.  */
  415.  
  416. static Slave *
  417. FindSlave(tkwin)
  418.     Tk_Window tkwin;        /* Token for desired slave. */
  419. {
  420.     Tcl_HashEntry *hPtr;
  421.     register Slave *slavePtr;
  422.     int new;
  423.  
  424.     hPtr = Tcl_CreateHashEntry(&slaveTable, (char *) tkwin, &new);
  425.     if (new) {
  426.     slavePtr = (Slave *) ckalloc(sizeof(Slave));
  427.     slavePtr->tkwin = tkwin;
  428.     slavePtr->masterPtr = NULL;
  429.     slavePtr->nextPtr = NULL;
  430.     slavePtr->x = slavePtr->y = 0;
  431.     slavePtr->relX = slavePtr->relY = 0.0;
  432.     slavePtr->width = slavePtr->height = 0;
  433.     slavePtr->relWidth = slavePtr->relHeight = 0.0;
  434.     slavePtr->anchor = TK_ANCHOR_NW;
  435.     slavePtr->borderMode = BM_INSIDE;
  436.     slavePtr->flags = 0;
  437.     Tcl_SetHashValue(hPtr, slavePtr);
  438.     Tk_CreateEventHandler(tkwin, StructureNotifyMask, SlaveStructureProc,
  439.         (ClientData) slavePtr);
  440.     Tk_ManageGeometry(tkwin, &placerType, (ClientData) slavePtr);
  441.     } else {
  442.     slavePtr = (Slave *) Tcl_GetHashValue(hPtr);
  443.     }
  444.     return slavePtr;
  445. }
  446.  
  447. /*
  448.  *----------------------------------------------------------------------
  449.  *
  450.  * UnlinkSlave --
  451.  *
  452.  *    This procedure removes a slave window from the chain of slaves
  453.  *    in its master.
  454.  *
  455.  * Results:
  456.  *    None.
  457.  *
  458.  * Side effects:
  459.  *    The slave list of slavePtr's master changes.
  460.  *
  461.  *----------------------------------------------------------------------
  462.  */
  463.  
  464. static void
  465. UnlinkSlave(slavePtr)
  466.     Slave *slavePtr;        /* Slave structure to be unlinked. */
  467. {
  468.     register Master *masterPtr;
  469.     register Slave *prevPtr;
  470.  
  471.     masterPtr = slavePtr->masterPtr;
  472.     if (masterPtr == NULL) {
  473.     return;
  474.     }
  475.     if (masterPtr->slavePtr == slavePtr) {
  476.     masterPtr->slavePtr = slavePtr->nextPtr;
  477.     } else {
  478.     for (prevPtr = masterPtr->slavePtr; ;
  479.         prevPtr = prevPtr->nextPtr) {
  480.         if (prevPtr == NULL) {
  481.         panic("UnlinkSlave couldn't find slave to unlink");
  482.         }
  483.         if (prevPtr->nextPtr == slavePtr) {
  484.         prevPtr->nextPtr = slavePtr->nextPtr;
  485.         break;
  486.         }
  487.     }
  488.     }
  489.     slavePtr->masterPtr = NULL;
  490. }
  491.  
  492. /*
  493.  *----------------------------------------------------------------------
  494.  *
  495.  * FindMaster --
  496.  *
  497.  *    Given a Tk_Window token, find the Master structure corresponding
  498.  *    to that token (making a new one if necessary).
  499.  *
  500.  * Results:
  501.  *    None.
  502.  *
  503.  * Side effects:
  504.  *    A new Master structure may be created.
  505.  *
  506.  *----------------------------------------------------------------------
  507.  */
  508.  
  509. static Master *
  510. FindMaster(tkwin)
  511.     Tk_Window tkwin;        /* Token for desired master. */
  512. {
  513.     Tcl_HashEntry *hPtr;
  514.     register Master *masterPtr;
  515.     int new;
  516.  
  517.     hPtr = Tcl_CreateHashEntry(&masterTable, (char *) tkwin, &new);
  518.     if (new) {
  519.     masterPtr = (Master *) ckalloc(sizeof(Master));
  520.     masterPtr->tkwin = tkwin;
  521.     masterPtr->slavePtr = NULL;
  522.     masterPtr->flags = 0;
  523.     Tcl_SetHashValue(hPtr, masterPtr);
  524.     Tk_CreateEventHandler(masterPtr->tkwin, StructureNotifyMask,
  525.         MasterStructureProc, (ClientData) masterPtr);
  526.     } else {
  527.     masterPtr = (Master *) Tcl_GetHashValue(hPtr);
  528.     }
  529.     return masterPtr;
  530. }
  531.  
  532. /*
  533.  *----------------------------------------------------------------------
  534.  *
  535.  * ConfigureSlave --
  536.  *
  537.  *    This procedure is called to process an argv/argc list to
  538.  *    reconfigure the placement of a window.
  539.  *
  540.  * Results:
  541.  *    A standard Tcl result.  If an error occurs then a message is
  542.  *    left in interp->result.
  543.  *
  544.  * Side effects:
  545.  *    Information in slavePtr may change, and slavePtr's master is
  546.  *    scheduled for reconfiguration.
  547.  *
  548.  *----------------------------------------------------------------------
  549.  */
  550.  
  551. static int
  552. ConfigureSlave(interp, slavePtr, argc, argv)
  553.     Tcl_Interp *interp;        /* Used for error reporting. */
  554.     Slave *slavePtr;        /* Pointer to current information
  555.                  * about slave. */
  556.     int argc;            /* Number of config arguments. */
  557.     char **argv;        /* String values for arguments. */
  558. {
  559.     register Master *masterPtr;
  560.     int c, result;
  561.     size_t length;
  562.     double d;
  563.  
  564.     result = TCL_OK;
  565.     if (Tk_IsTopLevel(slavePtr->tkwin)) {
  566.     Tcl_AppendResult(interp, "can't use placer on top-level window \"",
  567.         Tk_PathName(slavePtr->tkwin), "\"; use wm command instead",
  568.         (char *) NULL);
  569.     return TCL_ERROR;
  570.     }
  571.     for ( ; argc > 0; argc -= 2, argv += 2) {
  572.     if (argc < 2) {
  573.         Tcl_AppendResult(interp, "extra option \"", argv[0],
  574.             "\" (option with no value?)", (char *) NULL);
  575.         result = TCL_ERROR;
  576.         goto done;
  577.     }
  578.     length = strlen(argv[0]);
  579.     c = argv[0][1];
  580.     if ((c == 'a') && (strncmp(argv[0], "-anchor", length) == 0)) {
  581.         if (Tk_GetAnchor(interp, argv[1], &slavePtr->anchor) != TCL_OK) {
  582.         result = TCL_ERROR;
  583.         goto done;
  584.         }
  585.     } else if ((c == 'b')
  586.         && (strncmp(argv[0], "-bordermode", length) == 0)) {
  587.         c = argv[1][0];
  588.         length = strlen(argv[1]);
  589.         if ((c == 'i') && (strncmp(argv[1], "ignore", length) == 0)
  590.             && (length >= 2)) {
  591.         slavePtr->borderMode = BM_IGNORE;
  592.         } else if ((c == 'i') && (strncmp(argv[1], "inside", length) == 0)
  593.             && (length >= 2)) {
  594.         slavePtr->borderMode = BM_INSIDE;
  595.         } else if ((c == 'o')
  596.             && (strncmp(argv[1], "outside", length) == 0)) {
  597.         slavePtr->borderMode = BM_OUTSIDE;
  598.         } else {
  599.         Tcl_AppendResult(interp, "bad border mode \"", argv[1],
  600.             "\": must be ignore, inside, or outside",
  601.             (char *) NULL);
  602.         result = TCL_ERROR;
  603.         goto done;
  604.         }
  605.     } else if ((c == 'h') && (strncmp(argv[0], "-height", length) == 0)) {
  606.         if (argv[1][0] == 0) {
  607.         slavePtr->flags &= ~CHILD_HEIGHT;
  608.         } else {
  609.         if (Tk_GetPixels(interp, slavePtr->tkwin, argv[1],
  610.             &slavePtr->height) != TCL_OK) {
  611.             result = TCL_ERROR;
  612.             goto done;
  613.         }
  614.         slavePtr->flags |= CHILD_HEIGHT;
  615.         }
  616.     } else if ((c == 'i') && (strncmp(argv[0], "-in", length) == 0)) {
  617.         Tk_Window tkwin;
  618.         Tk_Window ancestor;
  619.  
  620.         tkwin = Tk_NameToWindow(interp, argv[1], slavePtr->tkwin);
  621.         if (tkwin == NULL) {
  622.         result = TCL_ERROR;
  623.         goto done;
  624.         }
  625.  
  626.         /*
  627.          * Make sure that the new master is either the logical parent
  628.          * of the slave or a descendant of that window, and that the
  629.          * master and slave aren't the same.
  630.          */
  631.  
  632.         for (ancestor = tkwin; ; ancestor = Tk_Parent(ancestor)) {
  633.         if (ancestor == Tk_Parent(slavePtr->tkwin)) {
  634.             break;
  635.         }
  636.         if (Tk_IsTopLevel(ancestor)) {
  637.             Tcl_AppendResult(interp, "can't place ",
  638.                 Tk_PathName(slavePtr->tkwin), " relative to ",
  639.                 Tk_PathName(tkwin), (char *) NULL);
  640.             result = TCL_ERROR;
  641.             goto done;
  642.         }
  643.         }
  644.         if (slavePtr->tkwin == tkwin) {
  645.         Tcl_AppendResult(interp, "can't place ",
  646.             Tk_PathName(slavePtr->tkwin), " relative to itself",
  647.             (char *) NULL);
  648.         result = TCL_ERROR;
  649.         goto done;
  650.         }
  651.         if ((slavePtr->masterPtr != NULL)
  652.             && (slavePtr->masterPtr->tkwin == tkwin)) {
  653.         /*
  654.          * Re-using same old master.  Nothing to do.
  655.          */
  656.         } else {
  657.         if ((slavePtr->masterPtr != NULL)
  658.             && (slavePtr->masterPtr->tkwin
  659.             != Tk_Parent(slavePtr->tkwin))) {
  660.             Tk_UnmaintainGeometry(slavePtr->tkwin,
  661.                 slavePtr->masterPtr->tkwin);
  662.         }
  663.         UnlinkSlave(slavePtr);
  664.         slavePtr->masterPtr = FindMaster(tkwin);
  665.         slavePtr->nextPtr = slavePtr->masterPtr->slavePtr;
  666.         slavePtr->masterPtr->slavePtr = slavePtr;
  667.         }
  668.     } else if ((c == 'r') && (strncmp(argv[0], "-relheight", length) == 0)
  669.         && (length >= 5)) {
  670.         if (argv[1][0] == 0) {
  671.         slavePtr->flags &= ~CHILD_REL_HEIGHT;
  672.         } else {
  673.         if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
  674.             result = TCL_ERROR;
  675.             goto done;
  676.         }
  677.         slavePtr->relHeight = d;
  678.         slavePtr->flags |= CHILD_REL_HEIGHT;
  679.         }
  680.     } else if ((c == 'r') && (strncmp(argv[0], "-relwidth", length) == 0)
  681.         && (length >= 5)) {
  682.         if (argv[1][0] == 0) {
  683.         slavePtr->flags &= ~CHILD_REL_WIDTH;
  684.         } else {
  685.         if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
  686.             result = TCL_ERROR;
  687.             goto done;
  688.         }
  689.         slavePtr->relWidth = d;
  690.         slavePtr->flags |= CHILD_REL_WIDTH;
  691.         }
  692.     } else if ((c == 'r') && (strncmp(argv[0], "-relx", length) == 0)
  693.         && (length >= 5)) {
  694.         if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
  695.         result = TCL_ERROR;
  696.         goto done;
  697.         }
  698.         slavePtr->relX = d;
  699.     } else if ((c == 'r') && (strncmp(argv[0], "-rely", length) == 0)
  700.         && (length >= 5)) {
  701.         if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
  702.         result = TCL_ERROR;
  703.         goto done;
  704.         }
  705.         slavePtr->relY = d;
  706.     } else if ((c == 'w') && (strncmp(argv[0], "-width", length) == 0)) {
  707.         if (argv[1][0] == 0) {
  708.         slavePtr->flags &= ~CHILD_WIDTH;
  709.         } else {
  710.         if (Tk_GetPixels(interp, slavePtr->tkwin, argv[1],
  711.             &slavePtr->width) != TCL_OK) {
  712.             result = TCL_ERROR;
  713.             goto done;
  714.         }
  715.         slavePtr->flags |= CHILD_WIDTH;
  716.         }
  717.     } else if ((c == 'x') && (strncmp(argv[0], "-x", length) == 0)) {
  718.         if (Tk_GetPixels(interp, slavePtr->tkwin, argv[1],
  719.             &slavePtr->x) != TCL_OK) {
  720.         result = TCL_ERROR;
  721.         goto done;
  722.         }
  723.     } else if ((c == 'y') && (strncmp(argv[0], "-y", length) == 0)) {
  724.         if (Tk_GetPixels(interp, slavePtr->tkwin, argv[1],
  725.             &slavePtr->y) != TCL_OK) {
  726.         result = TCL_ERROR;
  727.         goto done;
  728.         }
  729.     } else {
  730.         Tcl_AppendResult(interp, "unknown or ambiguous option \"",
  731.             argv[0], "\": must be -anchor, -bordermode, -height, ",
  732.             "-in, -relheight, -relwidth, -relx, -rely, -width, ",
  733.             "-x, or -y", (char *) NULL);
  734.         result = TCL_ERROR;
  735.         goto done;
  736.     }
  737.     }
  738.  
  739.     /*
  740.      * If there's no master specified for this slave, use its Tk_Parent.
  741.      * Then arrange for a placement recalculation in the master.
  742.      */
  743.  
  744.     done:
  745.     masterPtr = slavePtr->masterPtr;
  746.     if (masterPtr == NULL) {
  747.     masterPtr = FindMaster(Tk_Parent(slavePtr->tkwin));
  748.     slavePtr->masterPtr = masterPtr;
  749.     slavePtr->nextPtr = masterPtr->slavePtr;
  750.     masterPtr->slavePtr = slavePtr;
  751.     }
  752.     if (!(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
  753.     masterPtr->flags |= PARENT_RECONFIG_PENDING;
  754.     Tcl_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr);
  755.     }
  756.     return result;
  757. }
  758.  
  759. /*
  760.  *----------------------------------------------------------------------
  761.  *
  762.  * RecomputePlacement --
  763.  *
  764.  *    This procedure is called as a when-idle handler.  It recomputes
  765.  *    the geometries of all the slaves of a given master.
  766.  *
  767.  * Results:
  768.  *    None.
  769.  *
  770.  * Side effects:
  771.  *    Windows may change size or shape.
  772.  *
  773.  *----------------------------------------------------------------------
  774.  */
  775.  
  776. static void
  777. RecomputePlacement(clientData)
  778.     ClientData clientData;    /* Pointer to Master record. */
  779. {
  780.     register Master *masterPtr = (Master *) clientData;
  781.     register Slave *slavePtr;
  782.     int x, y, width, height, tmp;
  783.     int masterWidth, masterHeight, masterBW;
  784.     double x1, y1, x2, y2;
  785.  
  786.     masterPtr->flags &= ~PARENT_RECONFIG_PENDING;
  787.  
  788.     /*
  789.      * Iterate over all the slaves for the master.  Each slave's
  790.      * geometry can be computed independently of the other slaves.
  791.      */
  792.  
  793.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  794.         slavePtr = slavePtr->nextPtr) {
  795.     /*
  796.      * Step 1: compute size and borderwidth of master, taking into
  797.      * account desired border mode.
  798.      */
  799.  
  800.     masterBW = 0;
  801.     masterWidth = Tk_Width(masterPtr->tkwin);
  802.     masterHeight = Tk_Height(masterPtr->tkwin);
  803.     if (slavePtr->borderMode == BM_INSIDE) {
  804.         masterBW = Tk_InternalBorderWidth(masterPtr->tkwin);
  805.     } else if (slavePtr->borderMode == BM_OUTSIDE) {
  806.         masterBW = -Tk_Changes(masterPtr->tkwin)->border_width;
  807.     }
  808.     masterWidth -= 2*masterBW;
  809.     masterHeight -= 2*masterBW;
  810.  
  811.     /*
  812.      * Step 2:  compute size of slave (outside dimensions including
  813.      * border) and location of anchor point within master.
  814.      */
  815.  
  816.     x1 = slavePtr->x + masterBW + (slavePtr->relX*masterWidth);
  817.     x = x1 + ((x1 > 0) ? 0.5 : -0.5);
  818.     y1 = slavePtr->y + masterBW + (slavePtr->relY*masterHeight);
  819.     y = y1 + ((y1 > 0) ? 0.5 : -0.5);
  820.     if (slavePtr->flags & (CHILD_WIDTH|CHILD_REL_WIDTH)) {
  821.         width = 0;
  822.         if (slavePtr->flags & CHILD_WIDTH) {
  823.         width += slavePtr->width;
  824.         }
  825.         if (slavePtr->flags & CHILD_REL_WIDTH) {
  826.         /*
  827.          * The code below is a bit tricky.  In order to round
  828.          * correctly when both relX and relWidth are specified,
  829.          * compute the location of the right edge and round that,
  830.          * then compute width.  If we compute the width and round
  831.          * it, rounding errors in relX and relWidth accumulate.
  832.          */
  833.  
  834.         x2 = x1 + (slavePtr->relWidth*masterWidth);
  835.         tmp = x2 + ((x2 > 0) ? 0.5 : -0.5);
  836.         width += tmp - x;
  837.         }
  838.     } else {
  839.         width = Tk_ReqWidth(slavePtr->tkwin)
  840.             + 2*Tk_Changes(slavePtr->tkwin)->border_width;
  841.     }
  842.     if (slavePtr->flags & (CHILD_HEIGHT|CHILD_REL_HEIGHT)) {
  843.         height = 0;
  844.         if (slavePtr->flags & CHILD_HEIGHT) {
  845.         height += slavePtr->height;
  846.         }
  847.         if (slavePtr->flags & CHILD_REL_HEIGHT) {
  848.         /* 
  849.          * See note above for rounding errors in width computation.
  850.          */
  851.  
  852.         y2 = y1 + (slavePtr->relHeight*masterHeight);
  853.         tmp = y2 + ((y2 > 0) ? 0.5 : -0.5);
  854.         height += tmp - y;
  855.         }
  856.     } else {
  857.         height = Tk_ReqHeight(slavePtr->tkwin)
  858.             + 2*Tk_Changes(slavePtr->tkwin)->border_width;
  859.     }
  860.  
  861.     /*
  862.      * Step 3: adjust the x and y positions so that the desired
  863.      * anchor point on the slave appears at that position.  Also
  864.      * adjust for the border mode and master's border.
  865.      */
  866.  
  867.     switch (slavePtr->anchor) {
  868.         case TK_ANCHOR_N:
  869.         x -= width/2;
  870.         break;
  871.         case TK_ANCHOR_NE:
  872.         x -= width;
  873.         break;
  874.         case TK_ANCHOR_E:
  875.         x -= width;
  876.         y -= height/2;
  877.         break;
  878.         case TK_ANCHOR_SE:
  879.         x -= width;
  880.         y -= height;
  881.         break;
  882.         case TK_ANCHOR_S:
  883.         x -= width/2;
  884.         y -= height;
  885.         break;
  886.         case TK_ANCHOR_SW:
  887.         y -= height;
  888.         break;
  889.         case TK_ANCHOR_W:
  890.         y -= height/2;
  891.         break;
  892.         case TK_ANCHOR_NW:
  893.         break;
  894.         case TK_ANCHOR_CENTER:
  895.         x -= width/2;
  896.         y -= height/2;
  897.         break;
  898.     }
  899.  
  900.     /*
  901.      * Step 4: adjust width and height again to reflect inside dimensions
  902.      * of window rather than outside.  Also make sure that the width and
  903.      * height aren't zero.
  904.      */
  905.  
  906.     width -= 2*Tk_Changes(slavePtr->tkwin)->border_width;
  907.     height -= 2*Tk_Changes(slavePtr->tkwin)->border_width;
  908.     if (width <= 0) {
  909.         width = 1;
  910.     }
  911.     if (height <= 0) {
  912.         height = 1;
  913.     }
  914.  
  915.     /*
  916.      * Step 5: reconfigure the window and map it if needed.  If the
  917.      * slave is a child of the master, we do this ourselves.  If the
  918.      * slave isn't a child of the master, let Tk_MaintainWindow do
  919.      * the work (it will re-adjust things as relevant windows map,
  920.      * unmap, and move).
  921.      */
  922.  
  923.     if (masterPtr->tkwin == Tk_Parent(slavePtr->tkwin)) {
  924.         if ((x != Tk_X(slavePtr->tkwin))
  925.             || (y != Tk_Y(slavePtr->tkwin))
  926.             || (width != Tk_Width(slavePtr->tkwin))
  927.             || (height != Tk_Height(slavePtr->tkwin))) {
  928.         Tk_MoveResizeWindow(slavePtr->tkwin, x, y, width, height);
  929.         }
  930.  
  931.         /*
  932.          * Don't map the slave unless the master is mapped: the slave
  933.          * will get mapped later, when the master is mapped.
  934.          */
  935.  
  936.         if (Tk_IsMapped(masterPtr->tkwin)) {
  937.         Tk_MapWindow(slavePtr->tkwin);
  938.         }
  939.     } else {
  940.         if ((width <= 0) || (height <= 0)) {
  941.         Tk_UnmaintainGeometry(slavePtr->tkwin, masterPtr->tkwin);
  942.         Tk_UnmapWindow(slavePtr->tkwin);
  943.         } else {
  944.         Tk_MaintainGeometry(slavePtr->tkwin, masterPtr->tkwin,
  945.             x, y, width, height);
  946.         }
  947.     }
  948.     }
  949. }
  950.  
  951. /*
  952.  *----------------------------------------------------------------------
  953.  *
  954.  * MasterStructureProc --
  955.  *
  956.  *    This procedure is invoked by the Tk event handler when
  957.  *    StructureNotify events occur for a master window.
  958.  *
  959.  * Results:
  960.  *    None.
  961.  *
  962.  * Side effects:
  963.  *    Structures get cleaned up if the window was deleted.  If the
  964.  *    window was resized then slave geometries get recomputed.
  965.  *
  966.  *----------------------------------------------------------------------
  967.  */
  968.  
  969. static void
  970. MasterStructureProc(clientData, eventPtr)
  971.     ClientData clientData;    /* Pointer to Master structure for window
  972.                  * referred to by eventPtr. */
  973.     XEvent *eventPtr;        /* Describes what just happened. */
  974. {
  975.     register Master *masterPtr = (Master *) clientData;
  976.     register Slave *slavePtr, *nextPtr;
  977.  
  978.     if (eventPtr->type == ConfigureNotify) {
  979.     if ((masterPtr->slavePtr != NULL)
  980.         && !(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
  981.         masterPtr->flags |= PARENT_RECONFIG_PENDING;
  982.         Tcl_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr);
  983.     }
  984.     } else if (eventPtr->type == DestroyNotify) {
  985.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  986.         slavePtr = nextPtr) {
  987.         slavePtr->masterPtr = NULL;
  988.         nextPtr = slavePtr->nextPtr;
  989.         slavePtr->nextPtr = NULL;
  990.     }
  991.     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&masterTable,
  992.         (char *) masterPtr->tkwin));
  993.     if (masterPtr->flags & PARENT_RECONFIG_PENDING) {
  994.         Tcl_CancelIdleCall(RecomputePlacement, (ClientData) masterPtr);
  995.     }
  996.     masterPtr->tkwin = NULL;
  997.     ckfree((char *) masterPtr);
  998.     } else if (eventPtr->type == MapNotify) {
  999.     /*
  1000.      * When a master gets mapped, must redo the geometry computation
  1001.      * so that all of its slaves get remapped.
  1002.      */
  1003.  
  1004.     if ((masterPtr->slavePtr != NULL)
  1005.         && !(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
  1006.         masterPtr->flags |= PARENT_RECONFIG_PENDING;
  1007.         Tcl_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr);
  1008.     }
  1009.     } else if (eventPtr->type == UnmapNotify) {
  1010.     /*
  1011.      * Unmap all of the slaves when the master gets unmapped,
  1012.      * so that they don't keep redisplaying themselves.
  1013.      */
  1014.  
  1015.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  1016.         slavePtr = slavePtr->nextPtr) {
  1017.         Tk_UnmapWindow(slavePtr->tkwin);
  1018.     }
  1019.     }
  1020. }
  1021.  
  1022. /*
  1023.  *----------------------------------------------------------------------
  1024.  *
  1025.  * SlaveStructureProc --
  1026.  *
  1027.  *    This procedure is invoked by the Tk event handler when
  1028.  *    StructureNotify events occur for a slave window.
  1029.  *
  1030.  * Results:
  1031.  *    None.
  1032.  *
  1033.  * Side effects:
  1034.  *    Structures get cleaned up if the window was deleted.
  1035.  *
  1036.  *----------------------------------------------------------------------
  1037.  */
  1038.  
  1039. static void
  1040. SlaveStructureProc(clientData, eventPtr)
  1041.     ClientData clientData;    /* Pointer to Slave structure for window
  1042.                  * referred to by eventPtr. */
  1043.     XEvent *eventPtr;        /* Describes what just happened. */
  1044. {
  1045.     register Slave *slavePtr = (Slave *) clientData;
  1046.  
  1047.     if (eventPtr->type == DestroyNotify) {
  1048.     UnlinkSlave(slavePtr);
  1049.     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&slaveTable,
  1050.         (char *) slavePtr->tkwin));
  1051.     ckfree((char *) slavePtr);
  1052.     }
  1053. }
  1054.  
  1055. /*
  1056.  *----------------------------------------------------------------------
  1057.  *
  1058.  * PlaceRequestProc --
  1059.  *
  1060.  *    This procedure is invoked by Tk whenever a slave managed by us
  1061.  *    changes its requested geometry.
  1062.  *
  1063.  * Results:
  1064.  *    None.
  1065.  *
  1066.  * Side effects:
  1067.  *    The window will get relayed out, if its requested size has
  1068.  *    anything to do with its actual size.
  1069.  *
  1070.  *----------------------------------------------------------------------
  1071.  */
  1072.  
  1073.     /* ARGSUSED */
  1074. static void
  1075. PlaceRequestProc(clientData, tkwin)
  1076.     ClientData clientData;        /* Pointer to our record for slave. */
  1077.     Tk_Window tkwin;            /* Window that changed its desired
  1078.                      * size. */
  1079. {
  1080.     Slave *slavePtr = (Slave *) clientData;
  1081.     Master *masterPtr;
  1082.  
  1083.     if (((slavePtr->flags & (CHILD_WIDTH|CHILD_REL_WIDTH)) != 0)
  1084.         && ((slavePtr->flags & (CHILD_HEIGHT|CHILD_REL_HEIGHT)) != 0)) {
  1085.     return;
  1086.     }
  1087.     masterPtr = slavePtr->masterPtr;
  1088.     if (masterPtr == NULL) {
  1089.     return;
  1090.     }
  1091.     if (!(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
  1092.     masterPtr->flags |= PARENT_RECONFIG_PENDING;
  1093.     Tcl_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr);
  1094.     }
  1095. }
  1096.  
  1097. /*
  1098.  *--------------------------------------------------------------
  1099.  *
  1100.  * PlaceLostSlaveProc --
  1101.  *
  1102.  *    This procedure is invoked by Tk whenever some other geometry
  1103.  *    claims control over a slave that used to be managed by us.
  1104.  *
  1105.  * Results:
  1106.  *    None.
  1107.  *
  1108.  * Side effects:
  1109.  *    Forgets all placer-related information about the slave.
  1110.  *
  1111.  *--------------------------------------------------------------
  1112.  */
  1113.  
  1114.     /* ARGSUSED */
  1115. static void
  1116. PlaceLostSlaveProc(clientData, tkwin)
  1117.     ClientData clientData;    /* Slave structure for slave window that
  1118.                  * was stolen away. */
  1119.     Tk_Window tkwin;        /* Tk's handle for the slave window. */
  1120. {
  1121.     register Slave *slavePtr = (Slave *) clientData;
  1122.  
  1123.     if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
  1124.     Tk_UnmaintainGeometry(slavePtr->tkwin, slavePtr->masterPtr->tkwin);
  1125.     }
  1126.     Tk_UnmapWindow(tkwin);
  1127.     UnlinkSlave(slavePtr);
  1128.     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&slaveTable, (char *) tkwin));
  1129.     Tk_DeleteEventHandler(tkwin, StructureNotifyMask, SlaveStructureProc,
  1130.         (ClientData) slavePtr);
  1131.     ckfree((char *) slavePtr);
  1132. }
  1133.